home *** CD-ROM | disk | FTP | other *** search
/ User's Choice Windows CD / User's Choice Windows CD (CMS Software)(1993).iso / win_m_p / mews11.zip / ISEARCH.C < prev    next >
C/C++ Source or Header  |  1992-03-10  |  16KB  |  464 lines

  1. /*
  2.  * The functions in this file implement commands that perform incremental
  3.  * searches in the forward and backward directions.  This "ISearch" command
  4.  * is intended to emulate the same command from the original EMACS
  5.  * implementation (ITS).  Contains references to routines internal to
  6.  * SEARCH.C.
  7.  *
  8.  * REVISION HISTORY:
  9.  *
  10.  *    D. R. Banks 9-May-86
  11.  *    - added ITS EMACSlike ISearch
  12.  *
  13.  *    John M. Gamble 5-Oct-86
  14.  *    - Made iterative search use search.c's scanner() routine.
  15.  *      This allowed the elimination of bakscan().
  16.  *    - Put isearch constants into estruct.h
  17.  *    - Eliminated the passing of 'status' to scanmore() and
  18.  *      checknext(), since there were no circumstances where
  19.  *      it ever equalled FALSE.
  20.  *    Dan Corkill 6-Oct-87
  21.  *      - Changed character loop to terminate with extended characters
  22.  *        (thus arrow keys, and most commands behave intuitively).
  23.  *      - Changed META to be reread rather than simply aborting.
  24.  *      - Conditionalized VMS alternates for ^S and ^Q to only apply
  25.  *        to VMS ports.  (Allowing ^X as a synonym for ^S defeats some
  26.  *        of the benefits of the first change above.)
  27.  */
  28.  
  29. #include <stdio.h>
  30. #include "estruct.h"
  31. #include "eproto.h"
  32. #include "edef.h"
  33. #include "elang.h"
  34.  
  35. #if    ISRCH
  36.  
  37. #define CMDBUFLEN    256    /* Length of our command buffer */
  38.  
  39. /* A couple of "own" variables for re-eat */
  40. /* Hey, BLISS user, these were "GLOBAL", I made them "OWN". */
  41. static int    (PASCAL NEAR *saved_get_char)();/* Get character routine */
  42. static int    eaten_char = -1;        /* Re-eaten char */
  43.  
  44. /* A couple more "own" variables for the command string */
  45.  
  46. static int cmd_buff[CMDBUFLEN];        /* Save the command args here */
  47. static int cmd_offset;            /* Current offset into command buff */
  48. static int cmd_reexecute = -1;        /* > 0 if re-executing command */
  49.  
  50. /*
  51.  * Subroutine to do incremental reverse search.  It actually uses the
  52.  * same code as the normal incremental search, as both can go both ways.
  53.  */
  54. int PASCAL NEAR risearch(f, n)
  55.  
  56. int f,n;    /* prefix flag and argument */
  57.  
  58. {
  59.     register int    status;
  60.  
  61.     /* Make sure the search doesn't match where we already are:              */
  62.  
  63.     backchar(TRUE, 1);            /* Back up a character              */
  64.  
  65.     if (status = isearch(REVERSE))
  66.     mlerase();            /* If happy, just erase the cmd line  */
  67.     else
  68.     mlwrite(TEXT164);
  69. /*               "[search failed]" */
  70.     return (status);
  71. }
  72.  
  73. /* Again, but for the forward direction */
  74.  
  75. int PASCAL NEAR fisearch(f, n)
  76.  
  77. int f,n;    /* prefix flag and argument */
  78.  
  79. {
  80.     register int     status;
  81.  
  82.     if (status = isearch(FORWARD))
  83.     mlerase();            /* If happy, just erase the cmd line  */
  84.     else
  85.     mlwrite(TEXT164);
  86. /*               "[search failed]" */
  87.     return (status);
  88. }
  89.  
  90. /*
  91.  * Subroutine to do an incremental search.  In general, this works similarly
  92.  * to the older micro-emacs search function, except that the search happens
  93.  * as each character is typed, with the screen and cursor updated with each
  94.  * new search character.
  95.  *
  96.  * While searching forward, each successive character will leave the cursor
  97.  * at the end of the entire matched string.  Typing a Control-S
  98.  * will cause the next occurrence of the string to be searched for (where the
  99.  * next occurrence does NOT overlap the current occurrence).  A Control-R will
  100.  * change to a backwards search, META will terminate the search and Control-G
  101.  * will abort the search.  Rubout will back up to the previous match of the
  102.  * string, or if the starting point is reached first, it will delete the
  103.  * last character from the search string.
  104.  *
  105.  * While searching backward, each successive character will leave the cursor
  106.  * at the beginning of the matched string.  Typing a Control-R will search
  107.  * backward for the next occurrence of the string.  Control-S
  108.  * will revert the search to the forward direction.  In general, the reverse
  109.  * incremental search is just like the forward incremental search inverted.
  110.  *
  111.  * In all cases, if the search fails, the user will be feeped, and the search
  112.  * will stall until the pattern string is edited back into something that
  113.  * exists (or until the search is aborted).
  114.  */
  115. int PASCAL NEAR isearch(dir)
  116. int    dir;
  117. {
  118.     int            status;        /* Search status */
  119.     int            col;        /* prompt column */
  120.     register int    cpos;        /* character number in search string  */
  121.     register int    c;        /* current input character */
  122.     register int    expc;        /* function expanded input char          */
  123.     char        pat_save[NPAT];    /* Saved copy of the old pattern str  */
  124.     LINE        *curline;    /* Current line on entry          */
  125.     int            curoff;        /* Current offset on entry          */
  126.     int            init_direction;    /* The initial search direction          */
  127.     KEYTAB        *ktp;        /* The command bound to the key          */
  128.     register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
  129.  
  130.     /* Initialize starting conditions */
  131.  
  132.     cmd_reexecute = -1;            /* We're not re-executing (yet?)      */
  133.     cmd_offset = 0;            /* Start at the beginning of cmd_buff */
  134.     cmd_buff[0] = '\0';            /* Reset the command buffer          */
  135.     bytecopy(pat_save, pat, NPAT);    /* Save the old pattern string          */
  136.     curline = curwp->w_dotp;        /* Save the current line pointer      */
  137.     curoff  = curwp->w_doto;        /* Save the current offset          */
  138.     init_direction = dir;        /* Save the initial search direction  */
  139.  
  140.  
  141. start_over:    /* This is a good place to start a re-execution: */
  142.  
  143.     /*
  144.      * Ask the user for the text of a pattern,
  145.      * and remember the col.
  146.      */
  147.     col = promptpattern(TEXT165);
  148. /*                      "ISearch: " */
  149.  
  150.     cpos = 0;                    /* Start afresh              */
  151.     status = TRUE;                /* Assume everything's cool   */
  152.  
  153.     for (;;)                    /* ISearch per character loop */
  154.     {
  155.     /* Check for special characters first.
  156.          * That is, a control or ^X or FN or mouse function.
  157.      * Most cases here change the search.
  158.      */
  159.     c = ectoc(expc = get_char());
  160.  
  161.     if (expc == sterm) {            /* Want to quit searching?    */
  162.         setjtable();            /* Update the jump tables     */
  163.         return(TRUE);            /* Quit searching now          */
  164.     }
  165.  
  166.     if (expc == abortc)            /* If abort search request    */
  167.         break;                /* Quit searching          */
  168.  
  169.     if (expc == quotec)            /* Quote character?          */
  170.     {
  171.         c = ectoc(expc = get_char());    /* Get the next char          */
  172.     }
  173.     else if ((expc > 255 || expc == 0) && (c != '\t' && c != '\r'))
  174.     {
  175.         if (ktp = getbind(expc))
  176.             kfunc = ktp->k_ptr.fp;
  177.         else
  178.             kfunc = NULL;
  179.  
  180.         if (kfunc == forwsearch || kfunc == forwhunt || kfunc == fisearch || 
  181.         kfunc == backsearch || kfunc == backhunt || kfunc == risearch)
  182.             {
  183.                 dir = (kfunc == backsearch || kfunc == backhunt || kfunc == risearch)? REVERSE: FORWARD;
  184.  
  185.         /*
  186.          * if cpos == 0 then we are either just starting
  187.          * or starting over.  Use the original pattern
  188.          * in pat, which has either not been changed or
  189.          * has just been restored.  Find the length and
  190.          * re-echo the string.
  191.          */
  192.             if (cpos == 0)
  193.                 while (pat[cpos] != 0)
  194.                     col = echochar((int)pat[cpos++],col);
  195.  
  196.             status = scanmore(dir);
  197.             continue;
  198.             }
  199.             else if (kfunc == backdel)
  200.             {
  201.             if (cmd_offset <= 1)        /* Anything to delete?          */
  202.                 return(TRUE);        /* No, just exit          */
  203.  
  204.             cmd_offset -= 2;        /* Back up over the Rubout    */
  205.             cmd_buff[cmd_offset] = '\0';    /* Yes, delete last char   */
  206.             curwp->w_dotp = curline;    /* Reset the line pointer     */
  207.             curwp->w_doto = curoff;        /*  and the offset          */
  208.         dir = init_direction;        /* Reset the search direction */
  209.         bytecopy(pat, pat_save, NPAT);    /* Restore the old search str */
  210.         setjtable();            /* and its jump tables.       */
  211.         cmd_reexecute = 0;        /* Start the whole mess over  */
  212.         goto start_over;        /* Let it take care of itself */
  213.             }
  214.  
  215.             /* Presumably the key was uninteresting...*/
  216.  
  217.             reeat(expc);        /* Re-eat the char          */
  218.             return(TRUE);        /* And return the last status */
  219.     }
  220.  
  221.     /* I guess we got something to search for, so search for it          */
  222.  
  223.     pat[cpos++] = c;        /* put the char in the buffer */
  224.  
  225.     if (cpos >= NPAT)            /* too many chars in string?  */
  226.     {                    /* Yup.  Complain about it    */
  227.         mlwrite(TEXT166);
  228. /*                  "? Search string too long" */
  229.         bytecopy(pat, pat_save, NPAT);    /* Restore the old search str */
  230.         setjtable();            /* and its jump tables.       */
  231.         return(FALSE);            /* Return an error, but stay. */
  232.     }
  233.  
  234.     pat[cpos] = 0;                /* null terminate the buffer  */
  235.     col = echochar(c,col);            /* Echo the character          */
  236.     if (!status)                /* If we lost last time          */
  237.         TTbeep();                /* Feep again        */
  238.     else                    /* Otherwise, we must have won*/
  239.         status = checknext(c, dir);    /* See if still matches or find next */
  240.  
  241.     }    /* for {;;} */
  242.  
  243.     curwp->w_dotp = curline;        /* Reset the line pointer          */
  244.     curwp->w_doto = curoff;        /*  and the offset to original value  */
  245.     curwp->w_flag |= WFMOVE;        /* Say we've moved              */
  246.     update(FALSE);            /* And force an update              */
  247.     return (FALSE);
  248. }
  249.  
  250. /*
  251.  * This hack will search for the next occurrence of <pat> in the buffer,
  252.  * either forward or backward.  If we can't find any more matches, "point"
  253.  * is left where it was before.  If we do find a match, "point" will be at
  254.  * the end of the matched string for forward searches and at the beginning
  255.  * of the matched string for reverse searches.
  256.  */
  257.  
  258. int PASCAL NEAR scanmore(dir)
  259. int    dir;            /* direction to search            */
  260. {
  261.     register int    sts;        /* search status        */
  262.  
  263.     setjtable();            /* Set up fast search arrays    */
  264.  
  265.     sts = scanner(dir, (dir == REVERSE)? PTBEG: PTEND, 1);
  266.  
  267.     if (!sts)
  268.         TTbeep();    /* Feep if search fails       */
  269.  
  270.     return(sts);
  271. }
  272.  
  273. /*
  274.  * Trivial routine to insure that the next character in the search
  275.  * string is still true to whatever we're pointing to in the buffer.
  276.  * This routine will not attempt to move the "point" if the match
  277.  * fails, although it will implicitly move the "point" if we're
  278.  * forward searching, and find a match, since that's the way forward
  279.  * isearch works.  If we are reverse searching we compare all
  280.  * characters in the pattern string from "point" to the new end.
  281.  *
  282.  * If the compare fails, we return FALSE and call scanmore or something.
  283.  */
  284. int PASCAL NEAR checknext(chr, dir)
  285. int    chr;            /* Next char to look for         */
  286. int    dir;            /* Search direction             */
  287. {
  288.     LINE *curline;            /* current line during scan          */
  289.     int curoff;                /* position within current line          */
  290.     register char *patrn;        /* The entire search string (incl chr)   */
  291.     register int sts;            /* how well things go              */
  292.  
  293.     /* setup the local scan pointer to current "." */
  294.  
  295.     curline = curwp->w_dotp;        /* Get the current line structure     */
  296.     curoff  = curwp->w_doto;        /* Get the offset within that line    */
  297.  
  298.     if (dir == FORWARD)            /* If searching forward         */
  299.     {
  300.     if (sts = !boundry(curline, curoff, FORWARD))
  301.     {
  302.         /* Is it what we're looking for?      */
  303.         if (sts = eq(nextch(&curline, &curoff, FORWARD), chr))
  304.         {
  305.         curwp->w_dotp = curline;    /* Yes, set the buffer's point */
  306.         curwp->w_doto = curoff;        /*  to the matched character */
  307.         curwp->w_flag |= WFMOVE;    /* Say that we've moved */
  308.         }
  309.     }
  310.     }
  311.     else        /* Else, reverse search check. */
  312.     {
  313.         patrn = pat;
  314.         while (*patrn)    /* Loop for all characters in patrn   */
  315.         {
  316.         if ((sts = !boundry(curline, curoff, FORWARD)) == FALSE ||
  317.             (sts = eq(nextch(&curline, &curoff, FORWARD), *patrn)) == FALSE)
  318.             break;        /* Nope, just punt it then    */
  319.  
  320.         patrn++;
  321.         }
  322.     }
  323.  
  324.     /*
  325.      * If the 'next' character didn't fit in the pattern,
  326.      * let's go search for it somewhere else.
  327.      */
  328.     if (sts == FALSE)
  329.         sts = scanmore(dir);
  330.  
  331.     return(sts);        /* And return the status        */
  332. }
  333.  
  334. /*
  335.  * Routine to prompt for I-Search string.
  336.  */
  337.  
  338. int PASCAL NEAR promptpattern(prompt)
  339. char *prompt;
  340. {
  341.     char tpat[NPAT+20];
  342.  
  343.     strcpy(tpat, prompt);        /* copy prompt to output string */
  344.     strcat(tpat, " [");            /* build new prompt string */
  345.     expandp(pat, &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  346.     strcat(tpat, "]<META>: ");
  347.  
  348.     /* check to see if we are executing a command line */
  349.     if (!clexec) {
  350.     mlwrite(tpat);
  351.     }
  352.     return(strlen(tpat));
  353. }
  354.  
  355. /*
  356.  * Routine to echo i-search characters
  357.  */
  358.  
  359. int PASCAL NEAR echochar(c, col)
  360. int c;        /* character to be echoed */
  361. int col;    /* column to be echoed in */
  362. {
  363.     movecursor(term.t_nrow, col);    /* Position the cursor          */
  364.     if ((c < ' ') || (c == 0x7F))    /* Control character?    */
  365.     {
  366.     switch (c)            /* Yes, dispatch special cases*/
  367.     {
  368.       case '\r':            /* Newline          */
  369.         mlout('<');
  370.         mlout('N');
  371.         mlout('L');
  372.         mlout('>');
  373.         col += 3;
  374.         break;
  375.  
  376.       case '\t':            /* Tab              */
  377.         mlout('<');
  378.         mlout('T');
  379.         mlout('A');
  380.         mlout('B');
  381.         mlout('>');
  382.         col += 4;
  383.         break;
  384.  
  385.       default:        /* Vanilla control char and Rubout:   */
  386.         mlout('^');        /* Yes, output prefix          */
  387.         mlout(c ^ 0x40);    /* Make it "^X"              */
  388.         col++;        /* Count this char          */
  389.     }
  390.     } else
  391.     mlout(c);        /* Otherwise, output raw char */
  392.     TTflush();            /* Flush the output          */
  393.     return(++col);        /* return the new column no   */
  394. }
  395.  
  396. /*
  397.  * Routine to get the next character from the input stream.  If we're reading
  398.  * from the real terminal, force a screen update before we get the char.
  399.  * Otherwise, we must be re-executing the command string, so just return the
  400.  * next character.
  401.  */
  402.  
  403. int PASCAL NEAR get_char()
  404. {
  405.     int    c;
  406.  
  407.     /* See if we're re-executing: */
  408.  
  409.     if (cmd_reexecute >= 0)        /* Is there an offset?        */
  410.     if ((c = cmd_buff[cmd_reexecute++]) != 0)
  411.         return(c);            /* Yes, return any character    */
  412.  
  413.     /* We're not re-executing (or aren't any more).  Try for a real char
  414.      */
  415.     cmd_reexecute = -1;            /* Say we're in real mode again    */
  416.     update(FALSE);            /* Pretty up the screen        */
  417.     if (cmd_offset >= CMDBUFLEN-1)    /* If we're getting too big ...    */
  418.     {
  419.     mlwrite (TEXT167);        /* Complain loudly and bitterly    */
  420. /*               "? command too long" */
  421.     return(sterm);            /* And force a quit        */
  422.     }
  423.     c = getkey();            /* Get the next character    */
  424.  
  425.     cmd_buff[cmd_offset++] = c;        /* Save the char for next time    */
  426.     cmd_buff[cmd_offset] = '\0';    /* And terminate the buffer    */
  427.     return(c);                /* Return the character        */
  428. }
  429.  
  430. /*
  431.  * Hacky routine to re-eat a character.  This will save the character to be
  432.  * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  433.  */
  434.  
  435. /* Come here on the next term.t_getchar call: */
  436.  
  437. int PASCAL NEAR uneat()
  438. {
  439.     int c;
  440.  
  441.     term.t_getchar = saved_get_char;    /* restore the routine address          */
  442.     c = eaten_char;            /* Get the re-eaten char          */
  443.     eaten_char = -1;            /* Clear the old char              */
  444.     return(c);                /* and return the last char          */
  445. }
  446.  
  447. VOID PASCAL NEAR reeat(c)
  448. int    c;
  449. {
  450.     if (eaten_char != -1)        /* If we've already been here          */
  451.     return/*(NULL)*/;        /* Don't do it again              */
  452.     eaten_char = c;            /* Else, save the char for later      */
  453.     saved_get_char = term.t_getchar;    /* Save the char get routine          */
  454.     term.t_getchar = uneat;        /* Replace it with ours              */
  455. }
  456. #else
  457. int PASCAL NEAR isearch(dir)
  458.  
  459. int dir;
  460.  
  461. {
  462. }
  463. #endif
  464.